/*
 *  Ontology.h
 *  IncrementalAlgorithmSerial
 *
 *  Created by Roman Kutlak on 27/03/2010.
 *  Copyright 2010 Roman Kutlak. All rights reserved.
 *
 */

#ifndef _ONTOLOGY_H_
#define _ONTOLOGY_H_

#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <list>
#include <vector>
#include <memory>
#include <mutex>

#include "Entity.h"
#include "Cache.h"

//#define ONTOLOGY_DEBUG 1

namespace kutlak {
namespace roman {
namespace gre {
    
    class Statement 
    {
    public:
        Statement(const Vindex_t& subject = DEFAULT_VALUE,
                  const Vindex_t& predicate = DEFAULT_ATTRIBUTE,
                  const Vindex_t& object = DEFAULT_VALUE) :
        mSubject(subject), mPredicate(predicate), mObject(object) {};
        
        Statement(const std::string& subject,
                  const Vindex_t& predicate = DEFAULT_ATTRIBUTE,
                  const std::string& object = "") :
        mSubject(0), mPredicate(predicate), mObject(0) 
        {
            Voc& vocab = Voc::Instance();
            mSubject = vocab.Index(subject);
            mObject  = vocab.Index(object);
        };
        
        Statement(const Statement& rhs) :
        mSubject(rhs.mSubject), 
        mPredicate(rhs.mPredicate), 
        mObject(rhs.mObject) {}
        
        Statement(Statement&& rhs) :
        mSubject(std::move(rhs.mSubject)), 
        mPredicate(std::move(rhs.mPredicate)), 
        mObject(std::move(rhs.mObject)) {}
        
        // members
        Vindex_t mSubject;
        Vindex_t mPredicate;
        Vindex_t mObject;
        
        bool operator<(const Statement& rhs) const
        {
            if (mSubject < rhs.mSubject)
                return true;
            else if (mPredicate < rhs.mPredicate)
                return true;
            else 
                return (mObject < rhs.mObject);
        }
        
        bool operator==(const Statement& rhs) const 
        {
            return (mSubject == rhs.mSubject &&
                    mPredicate == rhs.mPredicate && 
                    mObject == rhs.mObject);
        }
        
        bool operator!=(const Statement& rhs) const
        { return !(*this == rhs); }
        
        // <subject> <predicate> <object> . \n
        friend std::ostream& operator<<(std::ostream& out, const Statement& obj) 
        {
            out << obj.mSubject;
            out << " ";
            out << obj.mPredicate;
            out << " ";
            out << obj.mObject;
            out << std::endl;
            
            return out;
        }
        
        friend std::istream& operator>>(std::istream& in, Statement& obj) 
        {
            in >> obj.mSubject;
            in >> obj.mPredicate;
            in >> obj.mObject;
            in >> std::ws;
            
            return in;
        }
        
        std::string ToRdf() const 
        {
            std::stringstream out;
            Voc& vocab = Voc::Instance();
            out << "<";
            out << vocab.String(mSubject);
            out << "> <";
            out << vocab.String(mPredicate);
            out << "> <";
            out << vocab.String(mObject);
            out << "> .";
            out << std::endl;
            
            return out.str();
        }
    };
    
    
    struct KeyT 
    {
        KeyT(Property p1 = Property(0, 0), Property p2 = Property(0, 0)) :
        first(p1), second(p2) {}
        
        KeyT(const KeyT& from) :
        first(from.first), second(from.second) {}
        
        KeyT(KeyT&& from) :
        first(std::move(from.first)), second(std::move(from.second)) {}
        
        KeyT operator=(KeyT from)
        { swap(*this, from); return *this; }
        
        friend void swap(KeyT& lhs, KeyT& rhs)
        {
            std::swap(lhs.first, rhs.first);
            std::swap(lhs.second, rhs.second);
        }
        
        friend std::istream& operator>>(std::istream& in, KeyT& key) 
        {
            in >> key.first;
            in >> key.second;
            
            return in;
        }
        
        friend bool operator<(const KeyT& left, const KeyT& right) 
        {
            if (left.first == right.first)
                return left.second < right.second;
            
            return left.first < right.first;
        }
        
        bool operator==(const KeyT& rhs) const 
        {
            return first == rhs.first && second == rhs.second;
        }
        
        Property first;
        Property second;
    };
    
    typedef Cache<KeyT, bool> CacheT;
    
    /*! \brief The Ontology class handles access to an ontology.
     *  The Ontology shoudl be able to check whether one value subsumes another.
     *  It should also be able to get a list of more- or less-specific values.
     */
    class Ontology 
    {
    public:
        // useful constants
        static const Vindex_t kType;
        
        static const Vindex_t kSubClassOf;
        
        static const Vindex_t kEquivalentClass;
        
        static const Vindex_t kEquivalentProperty;
        
        static const Vindex_t kObjectProperty;
        
        static const Vindex_t kRange;
        
        static const Vindex_t kThing;
        
        static const Vindex_t kPerson;
        
        static const Property kPersonType;
        
        static const Property kThingType;
        
        //! Container used for storing the statements
        typedef std::vector<Statement> ContainerT;
        
        
#pragma mark -
#pragma mark lifecycle
        //! Default constructor creates an instance that uses memory for storing the ontology.
        explicit Ontology(const std::string& fileName = "");
        
        Ontology(const Ontology& src);
        
        Ontology(Ontology&& src);
        
        Ontology& operator=(Ontology src);
        
    public:
        // Destructor
        ~Ontology() noexcept(true) ;
        
        friend void swap(Ontology& left, Ontology& right) ;
        
        
        bool operator==(const Ontology& rhs) const
        { return mStatements == rhs.mStatements; }
        
        bool operator!=(const Ontology& rhs) const 
        { return !(*this == rhs); }
        
#pragma mark -
#pragma mark operations
        std::vector<Property> MoreSpecificValues(const Property& property) const;
        std::vector<Property> LessSpecificValues(const Property& property) const;
        
        Property LessSpecificValue(const Property& property) const;
        
        Property BasicLevelValue(const Property& property) const;
        
        bool IsEquivalentClass(const Vindex_t& class1, const Vindex_t& class2) const;
        
        std::vector<Vindex_t> EquivalentClasses(const Vindex_t& c) const;
        
        // convenience method
        bool Subsumes(const std::string& parent, const std::string& child) const;
        
        bool Subsumes(const Property& parent, const Property& child) const;
        
        
        //! Method prints the model to stdout.
        void Print(int i = -1) const;
        
        //! Remove all statements.
        void Clear();
        
        size_t Size() const;
        
        
        /*! \brief Method returns true if the given entity is a named entity.
         *  Named entity is for example person, city, state or company.
         *  Simple method of checking is to inspect the type of the entity
         *  and possibly if it has a property foaf:name.
         *
         *  The method uses a static variable and so it is not thread safe.
         *  
         *  \param entity is the entity we want to check.
         *  \return true if the entity is a named entity.
         */
        bool IsNamedEntity(const EntityPtrT& entity) const;
        
        bool RangeIsNamedEntity(const Vindex_t& val) const;
        
        bool IsRelation(const Property& p) const;
        
        bool IsRelation(const Vindex_t& str) const;
        
        void AddStatement(const Statement& statement);
        
        void AddStatement(Statement&& statement);
        
        ContainerT Search(const Vindex_t& subject = DEFAULT_VALUE, 
                          const Vindex_t& predicate = DEFAULT_ATTRIBUTE,
                          const Vindex_t& object = DEFAULT_VALUE) const;
        
        ContainerT Search(const std::string& subject, 
                          const std::string& predicate = "",
                          const std::string& object = "") const;
        
        ContainerT Search(const std::string& subject, 
                          const Vindex_t& predicate = DEFAULT_ATTRIBUTE,
                          const std::string& object = "") const;
        
        static void SetDefault(const std::shared_ptr<Ontology>& ontology);
        
        static std::shared_ptr<Ontology> Default();
        
    private:
        
        bool CheckSubsumes(const Property& parent, const Property& child) const;
        
        static void PopulateNamedEntityList();
        
    public:
#pragma mark -
#pragma mark I/O
        
        friend std::ostream& operator<<(std::ostream& os, const Ontology& obj);
        
        friend std::istream& operator>>(std::istream& is, Ontology& obj);
        
        void Serialise(std::ostream& os) const;
        
        void Deserialise(std::istream& is);
        
        /*! \brief Method loads an rdf n-triple formatted file.
         \param fileName is the path to the ontology file.
         */
        void OpenFile(const std::string& fileName);
        
        std::istream& FromRdf(std::istream& in);
        
        //! Method saves the ontology into a file in the RDF ntriple format.
        void SaveFile(const std::string& fileName) const;
        
        std::ostream& ToRdf(std::ostream& out) const;
        
    private:
        ContainerT mStatements;
        mutable CacheT mSubsumptionCache;
        
        static std::once_flag sFlag;
        static std::vector<Property> sNamedEntityTypes;
        
        static std::shared_ptr<Ontology> sDefault;
    };
    
    typedef std::shared_ptr<Ontology> pOntology;
    
}
}
}
#endif
